package com.tengju.data.infrastructure.service;

import com.tengju.data.domain.model.alert.AlertEvent;
import com.tengju.data.domain.model.alert.AlertService;
import com.tengju.data.domain.model.report.*;
import com.tengju.data.domain.model.service.MarketBonusCalService;
import com.tengju.data.domain.model.service.UserStarService;
import com.tengju.data.domain.model.template.SubsidyTemplate;
import com.tengju.data.domain.model.user.*;
import com.tengju.data.domain.shared.tree.Forest;
import com.tengju.data.domain.shared.tree.StableTree;
import com.tengju.data.domain.shared.tree.Tree;
import com.tengju.data.domain.shared.tree.TreeNode;
import com.tengju.data.infrastructure.external.convert.SubsidyTemplateConvert;
import com.tengju.data.interfaces.api.MarketProfitApiService;
import com.tengju.data.interfaces.api.vo.SubsidyTemplateVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class MarketBonusCalServiceImpl implements MarketBonusCalService {

    private final MarketProfitApiService marketProfitApiService;
    private final UserStarService userStarService;
    private final UserStarFeatureSnapshotRepository userStarFeatureSnapshotRepository;
    private final AlertService alertService;
    private final AchievementBonusRepository marketBonusRepository;
    private final AchievementBonusCalMidInfoRepository achievementBonusCalMidInfoRepository;
    private final AchievementBonusRiskRepository achievementBonusRiskRepository;

    @Override
    public Forest<MarketReport> initMarketReport(LocalDateTime logTime, Integer month, AchievementBonusCalTypeEnum calType) {
        List<AchievementBonusCalMidInfo> achievementBonusCalMidInfoList = achievementBonusCalMidInfoRepository.listMidInfo(logTime, month, calType);
        List<SubsidyTemplateVO> subsidyTemplateVOList = marketProfitApiService.queryAllSubsidyTemplate();
        List<SubsidyTemplate> subsidyTemplateList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(subsidyTemplateVOList)) {
            subsidyTemplateList = subsidyTemplateVOList.stream()
                    .map(SubsidyTemplateConvert::convertTemplateVOToEntity)
                    .collect(Collectors.toList());
        }
        List<UserStarFeature> userStarFeatureList;
        if (AchievementBonusCalTypeEnum.REALTIME == calType) {
            userStarFeatureList = userStarService.listStarFeature();
        } else if (AchievementBonusCalTypeEnum.MONTH == calType ||
                AchievementBonusCalTypeEnum.MONTH_RISK == calType) {
            userStarFeatureList = userStarFeatureSnapshotRepository.listMonthStarFeature(month);
        } else {
            throw new AchievementBonusCalException("计算类型不存在，type:" + calType);
        }
        Map<UserIdCode, UserId> userIdMap = userStarService.listUserId();
        return initAchievementBonusReport(logTime,month,achievementBonusCalMidInfoList, userStarFeatureList, subsidyTemplateList,userIdMap);
    }

    @Override
    public void saveAchievementBonus(Forest<MarketReport> marketReportForest, int version) {
        Set<StableTree<MarketReport>> bigTeamTrees = marketReportForest.getStableTrees();
        for (StableTree<MarketReport> bigTeamTree : bigTeamTrees) {
            if (bigTeamTree.getRoot().isVirtualData()) {
                log.info("不合法数据，父节点不存在，idCode:{}",bigTeamTree.getRoot().getId().toString());
                continue;
            }
            if (bigTeamTree.getAllNodes().size() == 1) {
                MarketReport data = bigTeamTree.getRoot().getData();
                if (!data.getLeaderUserInfo().isBlack()) {
                    log.info("非黑卡大团队，直接过滤，idCode:{}",data.getLeaderIdCode().getValue());
                    continue;
                }
            }
            try {
                ArrayList<TreeNode<MarketReport>> allNodes = new ArrayList<>();
                findAllNode(bigTeamTree.getRoot(),allNodes);
                List<AchievementBonus> achievementBonusList = new ArrayList<>();
                for (TreeNode<MarketReport> node : allNodes) {
                    if (!node.isVirtualData() && node.getData().isCompleted()) {
                        achievementBonusList.addAll(node.getData().printMarketBonus(version));
                    }
                }
                marketBonusRepository.saveAchievementBonus(achievementBonusList);
            } catch (AchievementBonusCalException e) {
                AlertEvent event = new AlertEvent(e.getMessage());
                alertService.alert(event);
            } catch (Exception e) {
                throw e;
            }
        }

    }

    private void findAllNode(TreeNode<MarketReport> root, ArrayList<TreeNode<MarketReport>> allNodes) {
        allNodes.add(root);
        Set<TreeNode<MarketReport>> children = root.getChildren();
        if (CollectionUtils.isEmpty(children)) {
            return;
        }
        for (TreeNode<MarketReport> child : children) {
            findAllNode(child,allNodes);
        }
    }


    @Override
    public Forest<MarketReport> initAchievementBonusReport(LocalDateTime logTime,
                                                            Integer month,
                                                            List<AchievementBonusCalMidInfo> achievementBonusCalMidInfoList,
                                                            List<UserStarFeature> userStarFeatureList,
                                                            List<SubsidyTemplate> subsidyTemplateList,
                                                            Map<UserIdCode, UserId> userIdMap) {
        if (CollectionUtils.isEmpty(achievementBonusCalMidInfoList)) {
            return null;
        }
        Map<Long, List<AchievementBonusCalMidInfo>> idCodeMidInfoMap = achievementBonusCalMidInfoList.stream()
                .collect(Collectors.groupingBy(v -> v.getIdCode().getValue()));
        Forest<MarketReport> marketReportForest = new Forest();
        //商品模板数据
        Map<Integer,SubsidyTemplate> subsidyTemplateMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(subsidyTemplateList)) {
            subsidyTemplateMap = subsidyTemplateList.stream()
                    .collect(Collectors.toMap(k -> k.getSubsidyTemplateId().intValue(),
                            v -> v,
                            (v1,v2) -> v1));
        }
        //星级特征数据
        Map<Long,UserStarFeature> userStarFeatureMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(userStarFeatureList)) {
            userStarFeatureMap = userStarFeatureList.stream()
                    .collect(Collectors.toMap(k -> k.getIdCode().getValue(),
                            v -> v,
                            (v1,v2) -> v1));
        }

        List<Future<Tree<MarketReport>>> taskList = new ArrayList<>();
        for (Long idCode : idCodeMidInfoMap.keySet()) {
            List<AchievementBonusCalMidInfo> userAchievementBonusCalMidInfoList = idCodeMidInfoMap.get(idCode);
            SmallTeamAchievement sat = new SmallTeamAchievement();
            userAchievementBonusCalMidInfoList
                    .forEach(v -> sat.addAchievement(v.getTeamRatioType(),
                            v.getSmallTeamMoneyOut(),
                            v.getSmallTeamCancelMoneyOut(),
                            v.getSmallTeamCancelBeforeMoneyOut(),
                            v.getSelfMoneyOut()));
            AchievementBonusCalMidInfo midInfo = userAchievementBonusCalMidInfoList.get(0);
            UserIdCode userPIdCode = midInfo.getPIdCode();
            UserStarFeature userStarFeature = userStarFeatureMap.getOrDefault(idCode,new UserStarFeature(idCode));
            UserLevelInfo leaderUserLevelInfo = new UserLevelInfo(
                    UserIdCode.of(idCode),
                    userIdMap.get(UserIdCode.of(idCode)),
                    userPIdCode,
                    userPIdCode == null ? null : userIdMap.get(userPIdCode),
                    midInfo.getLevel(),
                    midInfo.getLevelChangeTime()
                    );
            MarketReport btr = new MarketReport(
                    UserIdCode.of(idCode),
                    leaderUserLevelInfo,
                    new MarketAchievement(month),
                    sat,
                    subsidyTemplateMap,
                    logTime,
                    month,
                    userStarFeature
            );
            Future<Tree<MarketReport>> task = marketReportForest.asyncPutNode(btr, userPIdCode);
            taskList.add(task);
        }
        taskList.forEach(task -> {
            try {
                task.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
        marketReportForest.finishSetUp();
        return marketReportForest;

    }

    @Override
    public void saveAchievementBonusRisk(Forest<MarketReport> marketReportForest, int version) {
        Set<StableTree<MarketReport>> bigTeamTrees = marketReportForest.getStableTrees();
        for (StableTree<MarketReport> bigTeamTree : bigTeamTrees) {
            if (bigTeamTree.getRoot().isVirtualData()) {
                log.info("不合法数据，父节点不存在，idCode:{}",bigTeamTree.getRoot().getId().toString());
                continue;
            }
            if (bigTeamTree.getAllNodes().size() == 1) {
                MarketReport data = bigTeamTree.getRoot().getData();
                if (!data.getLeaderUserInfo().isBlack()) {
                    log.info("非黑卡大团队，直接过滤，idCode:{}",data.getLeaderIdCode().getValue());
                    continue;
                }
            }
            try {
                ArrayList<TreeNode<MarketReport>> allNodes = new ArrayList<>();
                findAllNode(bigTeamTree.getRoot(),allNodes);
                List<AchievementBonus> achievementBonusList = new ArrayList<>();
                for (TreeNode<MarketReport> node : allNodes) {
                    if (!node.isVirtualData() && node.getData().isCompleted()) {
                        achievementBonusList.addAll(node.getData().printMarketBonus(version));
                    }
                }
                achievementBonusRiskRepository.saveAchievementBonusRisk(achievementBonusList);
            } catch (AchievementBonusCalException e) {
                AlertEvent event = new AlertEvent(e.getMessage());
                alertService.alert(event);
            } catch (Exception e) {
                throw e;
            }
        }
    }

    @Override
    public void calMarketBonus(Forest<MarketReport> marketReportForest) {
        Set<StableTree<MarketReport>> bigTeamTrees = marketReportForest.getStableTrees();
        for (StableTree<MarketReport> bigTeamTree : bigTeamTrees) {
            if (bigTeamTree.getRoot().isVirtualData()) {
                log.info("不合法数据，父节点不存在，idCode:{}", bigTeamTree.getRoot().getId().toString());
                continue;
            }
            if (bigTeamTree.countNodes() == 1) {
                MarketReport data = bigTeamTree.getRoot().getData();
                if (!data.getLeaderUserInfo().isBlack()) {
                    log.info("非黑卡大团队，直接过滤，idCode:{}", data.getLeaderIdCode().getValue());
                    continue;
                }
            }
            try {
                prepareAchievementData(bigTeamTree.getRoot());
                doCalMarketBonus(bigTeamTree.getRoot());
            } catch (AchievementBonusCalException e) {
                AlertEvent event = new AlertEvent(e.getMessage());
                alertService.alert(event);
            }
        }
    }

    private MarketReport prepareAchievementData(TreeNode<MarketReport> node) {
        Set<TreeNode<MarketReport>> children = node.getChildren();
        List<MarketReport> childrenReports = new ArrayList<>();
        MarketReport marketReport = node.getData();
        if (CollectionUtils.isEmpty(children) && !node.getData().getLeaderUserInfo().isBlack()) {
            log.info("客户非黑卡,且为叶子节点，{}", marketReport.getLeaderIdCode());
            return marketReport;
        }
        if (!node.getData().getLeaderUserInfo().isBlack() && !judgeNodeChain(node)) {
            log.info("客户非黑卡,且不为叶子节点,{},终止计算", marketReport.getLeaderIdCode());
            throw new AchievementBonusCalException("黑卡关系链错误，有中间节点为非黑卡，node:" + marketReport.getLeaderIdCode().toString());
        }
        if (!CollectionUtils.isEmpty(children)) {
            for (TreeNode<MarketReport> child : children) {
                childrenReports.add(prepareAchievementData(child));
            }
        }
        marketReport.prepareData(childrenReports);
        return marketReport;
    }


    private MarketReport doCalMarketBonus(TreeNode<MarketReport> node) {
        Set<TreeNode<MarketReport>> children = node.getChildren();
        MarketReport bigTeamReport = node.getData();
        if (CollectionUtils.isEmpty(children) && !node.getData().getLeaderUserInfo().isBlack()) {
            log.info("客户非黑卡,且为叶子节点，{}", bigTeamReport.getLeaderIdCode());
            return bigTeamReport;
        }
        if (!node.getData().getLeaderUserInfo().isBlack() && !judgeNodeChain(node)) {
            log.info("客户非黑卡,且不为叶子节点,{},终止计算", bigTeamReport.getLeaderIdCode());
            throw new AchievementBonusCalException("黑卡关系链错误，有中间节点为非黑卡，node:" + bigTeamReport.getLeaderIdCode().toString());
        }
        if (!CollectionUtils.isEmpty(children)) {
            for (TreeNode<MarketReport> child : children) {
                doCalMarketBonus(child);
            }
        }
        log.info("计算市场补贴节点:{}", bigTeamReport.getLeaderIdCode());
        Set<Integer> teamTypeSet = bigTeamReport.getTeamTypeSet();
        node.getData().calBonus();
        for (Integer teamRatioType : teamTypeSet) {
            setSettleParentAndAchievement(teamRatioType, node, node.getParent());
        }
        bigTeamReport.completeCal();
        return bigTeamReport;
    }

    private void setSettleParentAndAchievement(Integer teamRatioType, TreeNode<MarketReport> targetNode, TreeNode<MarketReport> upNode) {
        if (upNode == null) {
            return;
        }
        if (targetNode.getData().isBelongTo(upNode.getData(), teamRatioType)) {
            targetNode.getData().setSettleParent(teamRatioType, upNode.getData().getLeaderUserInfo());
            upNode.getData().combineSettleAchievement(teamRatioType, targetNode.getData());
            return;
        }
        setSettleParentAndAchievement(teamRatioType, targetNode, upNode.getParent());
    }

    /**
     * 判断用户关系链中是否有非黑卡
     * @param node
     * @return
     */
    private boolean judgeNodeChain(@NotNull TreeNode<MarketReport> node) {
        if (node.getData().getLeaderUserInfo().isBlack()) {
            return false;
        }
        Set<TreeNode<MarketReport>> children = node.getChildren();
        if (CollectionUtils.isEmpty(children)) {
            return true;
        }
        for (TreeNode<MarketReport> child : children) {
            if (!judgeNodeChain(child)) {
                return false;
            }
        }
        return true;
    }


}
